#include "stdafx.h"
#include "POP.h"
#include "Array.h"

namespace TableGUI_DATA
{
	TableListEntry* m_table_list;
	u32 m_table_list_size;

	struct EntrySizers
	{
		wxBoxSizer* main_sizer;
		wxBoxSizer* info_sizer;

		EntrySizers(int main_orient, int info_orient)
			: main_sizer(new wxBoxSizer(main_orient))
			, info_sizer(new wxBoxSizer(info_orient))
		{
		}

		EntrySizers& operator += (const EntrySizers& right)
		{
			if(main_sizer && right.main_sizer) main_sizer->Add(right.main_sizer);
			if(info_sizer && right.main_sizer) info_sizer->Add(right.info_sizer);

			return *this;
		}
	};

	EntrySizers GetValueBoxSizer(wxWindow* parent, MainTableEntry* table, u32 e);

	EntrySizers GetListBoxSizer(wxWindow* parent, MainTableEntry& table)
	{
		EntrySizers sizers(wxHORIZONTAL, wxVERTICAL);

		const ListEntryArgs& args = (ListEntryArgs&)table.args;

		wxChoice& choice = *new wxChoice(parent, wxID_ANY);
		table.gui = &choice;
		sizers.main_sizer->Add(&choice);
		
		if((table.type & TYPE_PROTO_INFO) == TYPE_PROTO_INFO)
		{
			for(u32 i=0; i<table.sublist_size; ++i)
			{
				wxBoxSizer& sub_sizer = *new wxBoxSizer(wxVERTICAL);

				table.sublist[i].parent = &table;

				MainTableEntry& stable = table.sublist[i];
				const ListEntryArgs& sargs = (ListEntryArgs&)stable.args;

				for(u32 j=0; j<sargs.values_count; ++j)
				{
					wxBoxSizer& info_sizer = *new wxBoxSizer(wxVERTICAL);

					info_sizer.AddSpacer(5);
					info_sizer.Add(new wxStaticText(parent, wxID_ANY, sargs.values[j].GetName()));

					sub_sizer.Add(&info_sizer);
				}

				stable.sizer = &sub_sizer;
				sizers.info_sizer->Add(&sub_sizer);
				sub_sizer.Show(false);

				EntrySizers data_sizers = GetValueBoxSizer(parent, table.sublist, i);
				sizers += data_sizers;
				data_sizers.main_sizer->Show(false);
			}
		}

		if((table.type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
		{
			for(u32 i=0; i<table.sublist_size; ++i) choice.Append(table.name.IsEmpty() ? wxString::Format("proto %d", i) : table.name);

			return sizers;
		}

		for(u32 i=0; i<args.values_count; ++i)
		{
			choice.Append(args.values[i].GetName());

			if(!(table.type & TYPE_WITHOUT_DATA) && args.values[i].id == table.value)
			{
				choice.SetSelection(i);
			}

			if(((ListEntryArgs&)args.values[i].sublist_table.args).values_count)
			{
				EntrySizers sub_sizers(wxHORIZONTAL, wxVERTICAL);
				args.values[i].sublist_table.parent = &table;
				sub_sizers.main_sizer->AddSpacer(5);
				sub_sizers += GetValueBoxSizer(parent, &args.values[i].sublist_table, 0);
				sizers += sub_sizers;

				args.values[i].sublist_table.sizer = sub_sizers.main_sizer;

				sub_sizers.main_sizer->Show(false);

				if(table.type & TYPE_WITHOUT_DATA)
				{
					if(args.values[i].sublist_table.type & TYPE_LIST)
					{
						if(((wxChoice*)args.values[i].sublist_table.gui)->GetSelection() != -1)
						{
							choice.SetSelection(i);
						}
					}
					else if(choice.GetSelection() == -1)
					{
						choice.SetSelection(i);
					}
				}
			}
		}

		/*
		if(choice.GetSelection() < 0 && table.size && !(table.type & TYPE_PROTO_ENTRY) 
			&& (table.type & TYPE_PROTOTYPE) != TYPE_PROTOTYPE && !(table.type & TYPE_WITHOUT_DATA))
		{
			 wxMessageBox(wxString::Format("Bad value: 0x%x", table.value), table.name);
		}
		*/

		return sizers;
	}

	EntrySizers GetValueBoxSizer(wxWindow* parent, MainTableEntry* table, u32 e)
	{
		EntrySizers sizers(wxHORIZONTAL, wxVERTICAL);

		if(table[e].type & TYPE_LIST)
		{
			sizers += GetListBoxSizer(parent, table[e]);
		}
		else if(table[e].type & TYPE_VALUE)
		{
			const ValueEntryArgs& args = (ValueEntryArgs&)table[e].args;

			wxSpinCtrl& spin_ctrl = *new wxSpinCtrl(parent, wxID_ANY);
			table[e].gui = &spin_ctrl;

			spin_ctrl.SetMin(0x80000000);
			spin_ctrl.SetMax(0x7fffffff);

			switch(table[e].type & TYPE_A1_MASK)
			{
			case TYPE_MIN_I: spin_ctrl.SetMin(args.min_connect_imm); break;
			case TYPE_MIN_L: spin_ctrl.SetMin(args.min_connect_table->value); break;
			}

			switch(table[e].type & TYPE_A2_MASK)
			{
			case TYPE_MAX_I: spin_ctrl.SetMax(args.max_connect_imm); break;
			case TYPE_MAX_L: spin_ctrl.SetMax(args.max_connect_table->value); break;
			}

			spin_ctrl.SetValue(table[e].value);
			sizers.main_sizer->Add(&spin_ctrl);
		}
		else if(table[e].type & TYPE_CHECK)
		{
			const CheckEntryArgs& args = (CheckEntryArgs&)table[e].args;

			wxCheckBox& check_box = *new wxCheckBox(parent, wxID_ANY, table[e].name, wxDefaultPosition, wxDefaultSize,
				wxCHK_3STATE | (args.enable_check_3 ? wxCHK_ALLOW_3RD_STATE_FOR_USER : 0));

			table[e].gui = &check_box;

			wxCheckBoxState state;

			if(args.check_1 == table[e].value)
			{
				state = wxCHK_UNCHECKED;
			}
			else if(args.check_2 == table[e].value)
			{
				state = wxCHK_CHECKED;
			}
			else if(args.enable_check_3 && args.check_3 == table[e].value)
			{
				state = wxCHK_UNDETERMINED;
			}
			else
			{
				state = wxCHK_UNDETERMINED;
			}

			check_box.Set3StateValue(state);
			sizers.main_sizer->Add(&check_box);
		}

		if(!table[e].sizer) table[e].sizer = sizers.main_sizer;

		if(table[e].gui)
		{
			table[e].gui->Enable(!(table[e].type & TYPE_CONST));
			table[e].gui->Show(!(table[e].type & TYPE_HIDE));
		}

		if((table[e].type & TYPE_PROTO_INFO) != TYPE_PROTO_INFO)
		{
			for(u32 i=0; i<table[e].sublist_size; ++i)
			{
				table[e].sublist[i].parent = &table[e];
				sizers += GetValueBoxSizer(parent, table[e].sublist, i);
			}
		}

		return sizers;
	}

	wxBoxSizer* GetTableEntryBoxSizer(wxWindow* parent, MainTableEntry* table, u32 e)
	{
		bool static_sizer = !(table[e].type & TYPE_CHECK);
		wxBoxSizer* main_sizer = 
			static_sizer ? new wxStaticBoxSizer(wxVERTICAL, parent, table[e].name) : new wxBoxSizer(wxVERTICAL);

		if(!static_sizer) main_sizer->AddSpacer(5);

		const EntrySizers sizers = GetValueBoxSizer(parent, table, e);
		main_sizer->Add(sizers.main_sizer);
		main_sizer->Add(sizers.info_sizer);

		return main_sizer;
	}

	wxBoxSizer* GetTableBoxSizer(wxWindow* parent, MainTableEntry* table, u32 table_size)
	{
		wxBoxSizer& main_sizer = *new wxBoxSizer(wxVERTICAL);

		for(u32 i=0; i<table_size; ++i)
		{
			main_sizer.Add(GetTableEntryBoxSizer(parent, table, i), wxSizerFlags().Expand());
			main_sizer.AddSpacer(5);
		}

		return &main_sizer;
	}

	struct ConnectManager : public wxWindow
	{
		MainTableEntry* GetConnection(MainTableEntry* src_table, u32 src_num)
		{
			MainTableEntry& table = src_table[src_num];

			if(table.connect_type == CONNECT_NONE) return NULL;

			return table.connect_table;
		}

		void ConnectEntries(s32 value, s32 src_sel, MainTableEntry* src_table, u32 src_num)
		{
			MainTableEntry& table = src_table[src_num];

			switch(table.connect_type)
			{
			case CONNECT_BY_VAL:
			case CONNECT_VAL_AS_SEL:
				if(src_sel < 0) return;
			break;
			}

			MainTableEntry* connect = GetConnection(src_table, src_num);

			if(!connect) return;

			MainTableEntry& dst = *connect;

			if(dst.type & TYPE_LIST)
			{
				int sel = -1;
				int sel2 = ((wxChoice*)dst.gui)->GetSelection();

				switch(table.connect_type)
				{
				case CONNECT_BY_SEL:
					sel = src_sel;
				break;

				case CONNECT_BY_VAL:
				{
					const ListEntryArgs& args = (ListEntryArgs&)dst.args;

					for(u32 i=0; i<args.values_count; ++i)
					{
						if(args.values[i].id != value) continue;

						sel = i;
						break;
					}
				}
				break;

				case CONNECT_SEL_AS_VAL:
					sel = value;
				break;

				case CONNECT_VAL_AS_SEL:
				{
					const ListEntryArgs& args = (ListEntryArgs&)dst.args;

					for(u32 i=0; i<args.values_count; ++i)
					{
						if(args.values[i].id != src_sel) continue;

						sel = i;
						break;
					}
				}
				break;
				}

				if(sel != sel2)
				{
					((wxChoice*)dst.gui)->SetSelection(sel);

					wxCommandEvent event( wxEVT_COMMAND_CHOICE_SELECTED );
					event.SetId( dst.gui->GetId() );
					dst.gui->GetEventHandler()->AddPendingEvent( event );
				}

				return;
			}
			
			if(dst.type & TYPE_VALUE)
			{
				u32 val;

				switch(table.connect_type)
				{
				case CONNECT_BY_VAL:
					val = value;
				break;

				case CONNECT_VAL_AS_SEL:
					val = src_sel;
				break;

				default: return;
				}

				if(((wxSpinCtrl*)dst.gui)->GetValue() != val)
				{
					((wxSpinCtrl*)dst.gui)->SetValue(val);

					wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED );
					event.SetId( dst.gui->GetId() );
					dst.gui->GetEventHandler()->AddPendingEvent( event );
				}
			}
			
			if(dst.type & TYPE_CHECK)
			{
				const CheckEntryArgs& args = (CheckEntryArgs&)dst.args;

				wxCheckBoxState state = wxCHK_UNDETERMINED;;

				switch(table.connect_type)
				{
				case CONNECT_BY_SEL:
					state = src_sel < 0 || src_sel > 3 ? wxCHK_UNDETERMINED : (wxCheckBoxState)src_sel;
				break;

				case CONNECT_BY_VAL:
					if(args.check_1 == value)
					{
						state = wxCHK_UNCHECKED;
					}
					else if(args.check_2 == value)
					{
						state = wxCHK_CHECKED;
					}
					else if(args.enable_check_3 && args.check_3 == value)
					{
						state = wxCHK_UNDETERMINED;
					}
					else
					{
						state = wxCHK_UNDETERMINED;
					}
				break;

				case CONNECT_SEL_AS_VAL:
					state = src_sel < 0 || src_sel > 3 ? wxCHK_UNDETERMINED : (wxCheckBoxState)src_sel;
				break;

				case CONNECT_VAL_AS_SEL:
					if(args.check_1 == src_sel)
					{
						state = wxCHK_UNCHECKED;
					}
					else if(args.check_2 == src_sel)
					{
						state = wxCHK_CHECKED;
					}
					else if(args.enable_check_3 && args.check_3 == src_sel)
					{
						state = wxCHK_UNDETERMINED;
					}
					else
					{
						state = wxCHK_UNDETERMINED;
					}
				break;
				}

				if(state != ((wxCheckBox*)dst.gui)->Get3StateValue())
				{
					((wxCheckBox*)dst.gui)->Set3StateValue(state);

					wxCommandEvent event( wxEVT_COMMAND_CHECKBOX_CLICKED );
					event.SetId( dst.gui->GetId() );
					dst.gui->GetEventHandler()->AddPendingEvent( event );
				}

				return;
			}
		}

		void ConnectEvent(MainTableEntry* src_table, u32 src_num, MainTableEntry& dst, bool show_list = true)
		{
			MainTableEntry& src = src_table[src_num];

			u32 value;
			if(src.type & TYPE_LIST)
			{
				const ListEntryArgs& args = (ListEntryArgs&)src.args;
				int sel = src.gui ? ((wxChoice*)src.gui)->GetSelection() : -1;

				for(u32 i=0; i<args.values_count; ++i)
				{
					if(args.values[i].sublist_table.sizer)
					{
						args.values[i].sublist_table.sizer->Show(sel == i && show_list);
					}

					if(((ListEntryArgs&)args.values[i].sublist_table.args).values_count)
					{
						ConnectEvent(&args.values[i].sublist_table, 0, *args.values[i].sublist_table.connected, sel == i && show_list);
					}
				}

				if((src.type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
                {
					if(sel >= 0)
					{
						wxCommandEvent event( wxEVT_COMMAND_CHOICE_SELECTED );
						event.SetId( src.sublist[sel].gui->GetId() );
						src.sublist[sel].gui->GetEventHandler()->AddPendingEvent( event );
					}
					else
					{
						for(u32 i=0; i<src.sublist_size; ++i)
						{
							wxCommandEvent event( wxEVT_COMMAND_CHOICE_SELECTED );
							event.SetId( src.sublist[i].gui->GetId() );
							src.sublist[i].gui->GetEventHandler()->AddPendingEvent( event );
						}
					}
                }
				else
				{
					if(src.type & TYPE_INFO && sel >= 0)
					{
						ValueTableEntry* list = args.values;
						u32 list_count = args.values_count;

						for(u32 i=0; i<list_count; ++i)
						{
							if(!(list[i].sublist_table.type & TYPE_LIST))
							{
								if(list[i].sublist_table.gui) list[i].sublist_table.gui->Show(i == sel);
								continue;
							}

							int lsel = -1;

							MainTableEntry& ltable = list[i].sublist_table;
							const ListEntryArgs& largs = (ListEntryArgs&)ltable.args;

							if(sel == i)
							{
								for(u32 j=0; j<largs.values_count; ++j)
								{
									if(ltable.value != largs.values[j].id) continue;

									lsel = j;
									break;
								}
							}
							else
							{
								const int csel = ((wxChoice*)ltable.gui)->GetSelection();
								if(csel >= 0) ltable.value = largs.values[csel].id;
							}

							((wxChoice*)list[i].sublist_table.gui)->SetSelection(lsel);
							
							wxCommandEvent event( wxEVT_COMMAND_CHOICE_SELECTED );
							event.SetId( list[i].sublist_table.gui->GetId() );
							list[i].sublist_table.gui->GetEventHandler()->AddPendingEvent( event );
						}
					}
					else if(src.type & TYPE_PROTO_ENTRY && src.gui)
					{
						int psel = ((wxChoice*)src.parent->gui)->GetSelection();
						if(src.sizer) src.sizer->Show(src_num == psel);
						if(psel < 0 || src.parent->sublist[psel].gui->GetId() != src.gui->GetId()) return;

						int val = (src.type & TYPE_PROTO_ENTRY_C) == TYPE_PROTO_ENTRY_C ? sel : -1;

						for(u32 i=0; i<src.parent->sublist_size; ++i)
						{
							if(i != psel) ((wxChoice*)src.parent->sublist[i].gui)->SetSelection(val);
							if(src.parent->sublist[i].sizer) src.parent->sublist[i].sizer->Show(i == psel);
						}
					}

					ConnectEntries(sel >= 0 ? args.values[sel].id : -1, sel, src_table, src_num);
				}

				if(sel < 0 || &dst == NULL) return;

				value = args.values[sel].id;
			}
			else if(src.type & TYPE_VALUE)
			{
				value = ((wxSpinCtrl*)src.gui)->GetValue();

				ConnectEntries(value, 0, src_table, src_num);
			}
			else if(src.type & TYPE_CHECK)
			{
				const CheckEntryArgs& args = (CheckEntryArgs&)src.args;

				value = -1;
				wxCheckBoxState state = ((wxCheckBox*)src.gui)->Get3StateValue();

				bool can_connect = false;

				switch(state)
				{
				case wxCHK_UNCHECKED:		value = args.check_1; can_connect = true; break;
				case wxCHK_CHECKED:			value = args.check_2; can_connect = true; break;
				case wxCHK_UNDETERMINED:	if(args.enable_check_3) { value = args.check_3; can_connect = true; } break;
				}

				ConnectEntries(value, state, src_table, src_num);
				if(!can_connect) return;
			}

			if((dst.type & TYPE_A1_MASK) == TYPE_MIN_L/* && dst.a1 == src_num*/)
			{
				((wxSpinCtrl*)dst.gui)->SetMin(value);
			}

			if((dst.type & TYPE_A2_MASK) == TYPE_MAX_L/* && dst.a2 == src_num*/)
			{
				((wxSpinCtrl*)dst.gui)->SetMax(value);
			}
		}

		bool ConnectEvent(MainTableEntry* table, u32 table_size, u32 id)
		{
			for(u32 i=0; i<table_size; ++i)
			{
				if(table[i].gui && table[i].gui->GetId() == id)
				{
					ConnectEvent(table, i, *table[i].connected);
					return true;
				}

				if(table[i].type & TYPE_LIST)
				{
					ListEntryArgs& args = (ListEntryArgs&)table[i].args;

					for(u32 j=0; j<args.values_count; ++j)
					{
						if(args.values[j].sublist_table.args.a1)
						{
							if(ConnectEvent(&args.values[j].sublist_table, 1, id)) return true;
						}
					}
				}

				if(table[i].sublist_size) if(ConnectEvent(table[i].sublist, table[i].sublist_size, id)) return true;
			}

			return false;
		}

		void ConnectEvent(wxCommandEvent& event)
		{
			for(u32 i=0; i<m_table_list_size; ++i)
			{
				if(ConnectEvent(m_table_list[i].table, m_table_list[i].table_size, event.GetId()))
				{
					wxWindow* wind = m_table_list[i].table->gui->GetParent();
					wxSize min_size = wind->GetSizer()->CalcMin();

					if(wind->GetParent()->GetClientSize() != min_size)
					{
						wind->GetParent()->SetMinSize(wxSize(0, 0));
						wind->GetParent()->SetClientSize(min_size);
						wind->GetParent()->SetMinSize(wind->GetSize());
					}

					wind->GetSizer()->RecalcSizes();
					return;
				}
			}
			
			wxMessageBox("Connection failed.");
		}
	};

	bool ConnectEntries(MainTableEntry& table)
	{
		if(!table.gui) return false;

		wxEventType type;
		
		if(table.type & TYPE_LIST)
		{
			type = wxEVT_COMMAND_CHOICE_SELECTED;
		}
		else if(table.type & TYPE_VALUE)
		{
			type = wxEVT_COMMAND_TEXT_UPDATED;
		}
		else if(table.type & TYPE_CHECK)
		{
			type = wxEVT_COMMAND_CHECKBOX_CLICKED;
		}
		else return false;

		table.gui->Connect(type, wxCommandEventHandler(ConnectManager::ConnectEvent), 0, table.gui);

		wxCommandEvent event( type );
		event.SetId( table.gui->GetId() );
		table.gui->GetEventHandler()->AddPendingEvent( event );

		return true;
	}

	void ConnectEntries(MainTableEntry* table, u32 table_size)
	{
		for(u32 i=0; i<table_size; ++i)
		{
			if(table[i].sublist_size) ConnectEntries(table[i].sublist, table[i].sublist_size);

			if((table[i].type & TYPE_A1_MASK) == TYPE_MIN_L)
			{
				const ValueEntryArgs& args = (ValueEntryArgs&)table[i].args;
				
				if((args.min_connect_table->type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
				{
					for(u32 j=0; j<args.min_connect_table->sublist_size; ++j)
					{
						args.min_connect_table->sublist[j].connected = &table[i];

						if(!ConnectEntries(args.min_connect_table->sublist[j]))
						{
							wxMessageBox("Prototype connection failed.", args.min_connect_table->sublist[j].name);
							exit(1);
						}
					}
				}
				else
				{
					args.min_connect_table->connected = &table[i];
					if(!ConnectEntries(*args.min_connect_table))
					{
						wxMessageBox("Connection failed.", args.min_connect_table->name);
						exit(1);
					}
				}
			}

			if((table[i].type & TYPE_A2_MASK) == TYPE_MAX_L)
			{
				const ValueEntryArgs& args = (ValueEntryArgs&)table[i].args;

				if((args.max_connect_table->type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
				{
					for(u32 j=0; j<args.max_connect_table->sublist_size; ++j)
					{
						args.max_connect_table->sublist[j].connected = &table[i];
						if(!ConnectEntries(args.max_connect_table->sublist[j]))
						{
							wxMessageBox("Prototype connection failed.", args.max_connect_table->sublist[j].name);
							exit(1);
						}
					}
				}
				else
				{
					args.max_connect_table->connected = &table[i];
					if(!ConnectEntries(*args.max_connect_table))
					{
						wxMessageBox("Connection failed.", args.max_connect_table->name);
						exit(1);
					}
				}
			}

			if(table[i].type & TYPE_LIST)
			{
				ConnectEntries(table[i]);

				const ListEntryArgs& args = (ListEntryArgs&)table[i].args;

				for(u32 j=0; j<args.values_count; ++j)
				{
					if(!args.values[j].sublist_table.args.a1) continue;

					ConnectEntries(&args.values[j].sublist_table, 1);
				}
			}
		}
	}

	void ConnectEntries()
	{
		for(u32 i=0; i<m_table_list_size; ++i)
		{
			ConnectEntries(m_table_list[i].table, m_table_list[i].table_size);
		}
	}

	void SwapValues(MainTableEntry& table)
	{
		if(!table.gui) return;

		if(table.type & TYPE_LIST)
		{
			const int sel = ((wxChoice*)table.gui)->GetSelection();
			const ListEntryArgs& args = (ListEntryArgs&)table.args;

			if(sel != -1)
			{
				table.value = args.values[sel].id;

				const ListEntryArgs& sargs = (ListEntryArgs&)args.values[sel].sublist_table.args;
				if(sargs.values_count) SwapValues(args.values[sel].sublist_table);
			}
		}
		else if(table.type & TYPE_VALUE)
		{
			table.value = ((wxSpinCtrl*)table.gui)->GetValue();
		}
		else if(table.type & TYPE_CHECK)
		{
			const CheckEntryArgs& args = (CheckEntryArgs&)table.args;

			switch(((wxCheckBox*)table.gui)->Get3StateValue())
			{
			case wxCHK_UNCHECKED:	table.value = args.check_1; break;
			case wxCHK_CHECKED:		table.value = args.check_2; break;
			case wxCHK_UNDETERMINED:if(args.enable_check_3) table.value = args.check_3; break;
			}
		}

		for(u32 i=0; i<table.sublist_size; ++i)
		{
			SwapValues(table.sublist[i]);
		}
	}

	void SwapValues()
	{
		for(u32 i=0; i<m_table_list_size; ++i)
		{
			for(u32 j=0; j<m_table_list[i].table_size; ++j) SwapValues(m_table_list[i].table[j]);
		}
	}
}

using namespace TableGUI_DATA;

TableGUI::TableGUI()
{
}

void TableGUI::Init(TableListEntry* table_list, u32 table_list_size)
{
	m_table_list = table_list;
	m_table_list_size = table_list_size;
}

wxBoxSizer* TableGUI::GetBoxSizer(wxWindow* parent)
{
	wxBoxSizer& main_sizer = *new wxBoxSizer(wxVERTICAL);

	for(u32 i=0; i<m_table_list_size; ++i)
	{
		main_sizer.Add(GetTableBoxSizer(parent, m_table_list[i].table, m_table_list[i].table_size), wxSizerFlags().Expand());
	}

	ConnectEntries();

	return &main_sizer;
}

bool TableGUI::Load(int preoffset, MainTableEntry* table_list, int table_num, bool check)
{
	MainTableEntry& table = table_list[table_num];
	bool read_value = table.size > 0 && !(table.type & TYPE_WITHOUT_DATA);
	bool value_finded = false;

	if(read_value)
	{
		table.value = 0;

		Read(preoffset + table.offset, &table.value, table.size);
	}

	if(table.type & TYPE_LIST)
	{
		const ListEntryArgs& args = (ListEntryArgs&)table.args;

		for(u32 i=0; i<args.values_count; ++i)
		{
			if(args.values[i].name.IsEmpty()) args.values[i].name = table.name;
			if(!((ListEntryArgs&)args.values[i].sublist_table.args).values_count) continue;
			if(args.values[i].sublist_table.name.IsEmpty()) args.values[i].sublist_table.name = args.values[i].name;

			if(!args.values[i].sublist_table.size && table.size && table.type & TYPE_WITHOUT_DATA)
			{
				args.values[i].sublist_table.size = table.size;
				args.values[i].sublist_table.offset = table.offset;
			}

			if(Load(preoffset, &args.values[i].sublist_table, 0, true))
			{
				if(read_value) continue;

				table.value = args.values[i].id;
				value_finded = true;
			}
		}
	}

	if((table.type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
	{
		const ListEntryArgs& args = (ListEntryArgs&)table.args;
		MainTableEntry* entries = table.sublist;
		ValueTableEntry* proto = args.values;
		u32 entries_count = table.sublist_size;
		u32 proto_count = args.values_count;

		for(u32 i=0; i<entries_count; ++i)
		{
			const ListEntryArgs& eargs = (ListEntryArgs&)entries[i].args;
			ValueTableEntry* list = eargs.values;

			if(!entries[i].size)
			{
				entries[i].offset = table.offset;
				entries[i].size = table.size;
			}

			for(u32 j=0; j<eargs.values_count; ++j)
			{
				list[j].name = proto[j % proto_count].name;
				list[j].name.Replace("%num", wxString::Format("%d", j));

				wxString func = wxEmptyString;

				if(proto[j % proto_count].sublist_table.args.a1 != ARG_NONE)
				{
					func = ((wxString (*)(u32))proto[j % proto_count].sublist_table.args.a1)(list[j].id);
				}

				list[j].name.Replace("%func", func);
			}
		}
	}

	if(table.type & TYPE_INFO)
	{
		const InfoEntryArgs& args = (InfoEntryArgs&)table.args;

		ValueTableEntry* proto = args.proto;
		ValueTableEntry* list = args.list;
		MainTableEntry* info = table.sublist;
		u32 proto_count = args.proto_count;
		u32 list_count = args.list_count;
		u32 info_count = table.sublist_size;

		for(u32 i=0, n=0; i<list_count && i<info_count; ++i)
		{
			if(!(list[i].sublist_table.type & TYPE_LIST)) continue;

			list[i].sublist_table.connect_table = &info[i];
			list[i].sublist_table.connect_type = CONNECT_BY_SEL;
			n++;
		}

		for(u32 i=0; i<info_count; ++i)
		{
			ListEntryArgs& iargs = (ListEntryArgs&)info[i].args;
			iargs.values = proto;
			iargs.values_count = proto_count;
		}
	}

	for(u32 i=0; i<table.sublist_size; ++i)
	{
		Load(preoffset, table.sublist, i);
	}

	if(!check) return true;
	if(!read_value) return value_finded;

	if(table.type & TYPE_LIST)
	{
		const ListEntryArgs& args = (ListEntryArgs&)table.args;

		for(u32 i=0; i<args.values_count; ++i)
		{
			if(args.values[i].id == table.value) return true;
		}

		return false;
	}

	if(table.type & TYPE_VALUE)
	{
		return true;
	}

	return true;
}

bool TableGUI::Load()
{
	for(u32 i=0; i<m_table_list_size; ++i)
	{
		u32 preoffset = 0;
		if(m_table_list[i].offset)
		{
			for(u32 j=0; j<m_table_list[i].offset_size; ++j)
			{
				if(m_table_list[i].offset[j].GetOffset)
				{
					void* buf = new u8[m_table_list[i].offset[j].size];
					Read(m_table_list[i].offset[j].read_offset, buf, m_table_list[i].offset[j].size);
					int offs = m_table_list[i].offset[j].GetOffset(buf);
					free(buf);

					if(offs < 0) continue;

					preoffset = m_table_list[i].cur_offset = offs;

					break;
				}

				u32 buf;
				Read(m_table_list[i].offset[j].read_offset, &buf, m_table_list[i].offset[j].size);

				if(buf == m_table_list[i].offset[j].value)
				{
					preoffset = m_table_list[i].cur_offset = m_table_list[i].offset[j].offset;
					break;
				}
			}

			if(m_table_list[i].cur_offset < 0)
			{
				wxMessageBox("Offset loading error.");
				return false;
			}
		}

		for(u32 j=0; j<m_table_list[i].table_size; ++j)
		{
			Load(preoffset, m_table_list[i].table, j);
		}
	}

	return true;
}

void TableGUI::Save(int preoffset, MainTableEntry& table)
{
	if((table.type & TYPE_PROTOTYPE) == TYPE_PROTOTYPE)
	{
		int sel = ((wxChoice*)table.gui)->GetSelection();
		ListEntryArgs& args = (ListEntryArgs&)table.args;

		if(sel < 0) return;

		Write(preoffset + table.sublist[sel].offset, &table.sublist[sel].value, table.sublist[sel].size);

		return;
	}

	bool write_value = table.size > 0 && !(table.type & TYPE_WITHOUT_DATA);

	if(write_value)
	{
		Write(preoffset + table.offset, &table.value, table.size);
	}

	if(table.type & TYPE_LIST)
	{
		ListEntryArgs& args = (ListEntryArgs&)table.args;
		int sel = ((wxChoice*)table.gui)->GetSelection();

		if(sel >= 0)
		{
			args.values[sel].sublist_table.offset = table.offset;
			if(sel >= 0 && ((ListEntryArgs&)args.values[sel].sublist_table.args).values_count)
			{
				Save(preoffset, args.values[sel].sublist_table);
			}
		}
	}

	for(u32 i=0; i<table.sublist_size; ++i)
	{
		Save(preoffset, table.sublist[i]);
	}
}

void TableGUI::Save()
{
	SwapValues();

	for(u32 i=0; i<m_table_list_size; ++i)
	{
		u32 preoffset = m_table_list[i].cur_offset >= 0 ? m_table_list[i].cur_offset : 0;
		for(u32 j=0; j<m_table_list[i].table_size; ++j)
		{
			Save(preoffset, m_table_list[i].table[j]);
		}
	}
}

bool FileTableGUI::Open(const wxString& path)
{
	return f.Open(path, wxFile::read_write);
}

void FileTableGUI::Close()
{
	f.Close();
}

void FileTableGUI::Write(u32 offset, const void* src, u32 size)
{
	f.Seek(offset);
	f.Write(src, size);
}

void FileTableGUI::Read(u32 offset, void* dst, u32 size)
{
	f.Seek(offset);
	f.Read(dst, size);
}

bool ProcessTableGUI::Open(const wxString& process)
{
	m_proc_manager.Update();

	return m_proc_manager.Open(process);
}

void ProcessTableGUI::Close()
{
	m_proc_manager.Close();
}

void ProcessTableGUI::Write(u32 offset, const void* src, u32 size)
{
	m_proc_manager.Write(m_proc_manager.GetBasicAddr() + offset, src, size);
}

void ProcessTableGUI::Read(u32 offset, void* dst, u32 size)
{
	m_proc_manager.Read(m_proc_manager.GetBasicAddr() + offset, dst, size);
}